<template>
  <div>
    <h3>单一文件上传 缩略图</h3>
    <div class="upload_div">
      <input
        ref="fileInput"
        type="file"
        class="upload_input"
        @change="changeFile"
      />
      <div class="upload_button_box">
        <button
          class="upload_button select"
          @click="chooseFile"
          :class="[{ disabled: disabledStyle }]"
        >
          选择文件
        </button>
        <button
          class="upload_button upload"
          @click="uploadFile"
          :class="[{ loading: loadingStyle }]"
        ></button>
      </div>
      <div class="upload_tip" v-if="uploadTipVisible">
        只能上传PNG/JPG/JPEG格式的图片，且大小不能超过10MB
      </div>

      <div class="abbr_img" v-if="abbrImgVisible">
        <img :src="imgSrc" width="150" height="87.5" />
      </div>
    </div>
  </div>
</template>

<script>
import { uploadSingleFileWithHashFilename } from "@/api/upload/AbbrSingleUploader.js";
// npm i spark-md5 --save
import SparkMD5 from "spark-md5";

export default {
  mounted() {},
  data() {
    return {
      uploadTipVisible: true,
      abbrImgVisible: false,
      imgSrc: null,
      files: [],
      disabledStyle: false,
      loadingStyle: false,
    };
  },
  props: ["onBeforeUpload"],
  methods: {
    chooseFile() {
      this.$refs.fileInput.click();
    },
    encodeImageFileToBase64(file) {
      return new Promise((resolve) => {
        let fileReader = new FileReader();
        fileReader.readAsDataURL(file);
        fileReader.onload = (event) => {
          resolve(event.target.result);
        };
      });
    },
    generateHashFileName(file) {
      return new Promise((resovle) => {
        let fileReader = new FileReader();
        fileReader.readAsArrayBuffer(file);
        fileReader.onload = (event) => {
          let buffer = event.target.result;
          let spark = new SparkMD5.ArrayBuffer();

          spark.append(buffer);
          let hash = spark.end();

          let suffix = /\.([a-zA-Z0-9]+)$/.exec(file.name)[1];

          resovle({
            buffer,
            hash,
            suffix,
            filename: `${hash}.${suffix}`,
          });
        };
      });
    },
    async changeFile(event) {
      this.files.length = 0;

      const file = event.srcElement.files[0];

      if (!file) return;

      this.hideTip();

      const base64 = await this.encodeImageFileToBase64(file);
      // 缩略图展示
      this.imgSrc = base64;

      this.files.push({
        name: file.name,
        file: file,
      });
    },
    async uploadFile() {
      // 如果没有选择文件，则提示先选择文件
      if (this.files.length == 0) {
        this.$message({
          type: "info",
          message: "请先选择文件",
        });
        return;
      }

      // 如果已经选择文件，则判断文件是否符合要求
      const file = this.files[0].file;

      let shouldUpload = true;
      if (this.onBeforeUpload) {
        shouldUpload = this.onBeforeUpload(file);
      }

      if (!shouldUpload) {
        this.$message({
          type: "warning",
          message: "文件不符合要求",
        });
        return;
      }
      // 根据文件内容生成MD5格式的文件名
      let { filename } = await this.generateHashFileName(file);

      // 如果文件符合要求，则利用formData上传文件
      const formData = new FormData();
      formData.append("filename", filename);
      formData.append("file", file);

      this.setButtonEnableStatus(false);
      // 给后端发起上传请求
      uploadSingleFileWithHashFilename(formData)
        .then((data) => {
          if (data.code == 200) {
            this.$message({
              type: "success",
              message: "上传文件成功! " + data.data,
            });
            return;
          }
          return Promise.reject(data.msg);
        })
        .catch((reason) => {
          this.$message({
            type: "error",
            message: "文件上传失败：" + reason,
          });
        })
        .finally(() => {
          this.clearImg();
          this.setButtonEnableStatus(true);
        });
    },
    showTip() {
      this.uploadTipVisible = true;
      this.abbrImgVisible = false;
    },
    hideTip() {
      this.uploadTipVisible = false;
      this.abbrImgVisible = true;
    },
    clearImg() {
      this.showTip();
      this.files.length = 0;
      this.imgSrc = null;
      // 在上传完一个文件后，如果再选择同一个文件，则fileInput不会触发change事件
      // 所以在这里清空fileInput的值，就可以在上传完一个文件后，选择同一个文件时，
      // 能触发fileInput的change事件
      this.$refs.fileInput.value = null;
    },
    setButtonEnableStatus(status) {
      this.disabledStyle = !status;
      this.loadingStyle = !status;
    },
  },
};
</script>

<style scoped>
.upload_div {
  margin: auto;
  width: 400px;
  height: 150px;
  border: #ccc dashed 1px;
}
.upload_input {
  display: none;
}
.upload_button_box button {
  margin: 5px;
}
.upload_button {
  size: 16px;
  padding: 5px;
  border-radius: 5px;
  border: #ccc solid 1px;
}
.upload_button:active {
  size: 16px;
  border-radius: 5px;
  border: #ccc outset 1px;
}
.select {
  background-color: #66cef6;
}
.upload {
  background-color: #cafe4f;
}
.upload::after {
  content: "上传到服务器";
}
.upload_tip {
  font-size: 12px;
}
.upload_list {
  font-size: 12px;
}

.disabled {
  pointer-events: none;
  background-color: #ccc;
}

.loading {
  width: 23px;
  height: 23px;
  border: 5px solid #ccc;
  border-bottom-color: #ff3d00;
  border-radius: 50%;
  display: inline-block;
  background: #fff;
  animation: rotation 0.7s linear infinite;
  pointer-events: none;
}
.loading::after {
  content: "";
}

@keyframes rotation {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
</style>